import { ComputeOptions, initOptions, Options } from "../core";
import * as booleanOperators from "../operators/expression/boolean";
import * as comparisonOperators from "../operators/expression/comparison";
import * as projectionOperators from "../operators/projection";
import * as queryOperators from "../operators/query";
import { AnyObject } from "../types";
import { AsyncCursor, AsyncCursorImpl } from "./Cursor";
import { QueryImpl } from "../query/_internal";
import { AsyncLazy, AsyncSource, isOpenableAsyncIterable } from "./lazy";

/**
 * Represents an async query object used to filter and match documents based on specified criteria.
 * All query, projection, and related expression operators are preloaded into the context by default.
 * 
 * When used with an OpenableAsyncIterable source, the query conditions are passed to the source
 * before iteration begins. This allows data sources like database connections to optimize their
 * data retrieval based on the query conditions, rather than fetching all data and filtering in memory.
 *
 * @example
 * ```typescript
 * const query = new AsyncQuery({ age: { $gt: 18 } });
 * for await (const result of query.findAsync(collection, projection)) {
 *   console.log(result);
 * }
 * ```
 */
export class AsyncQuery extends QueryImpl {
  #_condition: AnyObject;
  /**
   * Creates an instance of the async query with the specified condition and options.
   * This object is preloaded with all query and projection operators.
   *
   * @param condition - The query condition object used to define the criteria for matching documents.
   * @param options - Optional configuration settings to customize the query behavior.
   */
  constructor(condition: AnyObject, options?: Partial<Options>) {
    // 创建一个新的选项对象，确保我们可以修改它
    const fopts = options instanceof ComputeOptions
      ? { ...options.getOptions() }
      : { ...options };
    
    // 设置异步标志
    fopts.isAsync = true;
    
    const opts = initOptions(fopts);
    opts.context
      .addExpressionOps(booleanOperators)
      .addExpressionOps(comparisonOperators)
      .addProjectionOps(projectionOperators)
      .addQueryOps(queryOperators);
    super(condition, opts);
    this.#_condition = condition;
  }

  /**
   * Returns an async cursor for iterating over the items in the given collection that match the query criteria.
   * 
   * When the collection is an OpenableAsyncIterable, the query conditions are passed to the collection
   * via the addCondition method before iteration begins. This allows the data source to optimize its
   * data retrieval based on the query conditions.
   *
   * @typeParam T - The type of the items in the resulting cursor.
   * @param collection - The source collection to search through.
   * @param projection - An optional object specifying fields to include or exclude
   *                      in the returned items.
   * @returns An `AsyncCursor` instance for iterating over the matching items.
   */
  findAsync<T>(collection: AsyncSource, projection?: AnyObject): AsyncCursor<T> {
    const source = AsyncLazy(collection);
    if(isOpenableAsyncIterable(collection)){
      source.addCondition(this.#_condition)
    }
    const cursor = new AsyncCursorImpl<T>(
      source,
      (o: any) => this.test(o),
      projection || {},
      (this as any)['#options'] // 访问父类的私有属性
    );

    return cursor as AsyncCursor<T>;
  }
}